﻿using SuperSocialShare.Helper;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Text;
using System.Threading.Tasks;

namespace SuperSocialShare
{
    internal class HttpWebRequester
    {

        public event RequestCompletedEventHandler RequestCompleted;
        private object _userData;
        private string _postBoundary;//post header的boundary

        /// <summary>
        /// 获取或设置以Post方式请求数据时的ContentType
        /// </summary>
        public ContentType PostContentType { set; get; }

        /// <summary>
        /// 获取或设置自定义Authorization 标头
        /// </summary>
        public string Authorization { set; get; }

        private List<RequestParameter> _parameters;
        /// <summary>
        /// 获取请求的参数
        /// </summary>
        public List<RequestParameter> Parameters
        {
            get
            {
                _parameters = _parameters ?? new List<RequestParameter>();
                return _parameters;
            }
        }

        /// <summary>
        /// 异步获取网络数据
        /// </summary>
        /// <param name="url">网路地址</param>
        /// <param name="userData">异步操作的用户标识</param>
        public void BeginRequest(String url, object userData, string method = "GET")
        {
            _userData = userData;
            BeginRequest(url, method);
        }

        /// <summary>
        /// 异步获取网络数据
        /// </summary>
        /// <param name="url">网络地址</param>
        public void BeginRequest(string url, string method = "GET")
        {
            HttpWebRequest request = null;
            
            if (method == "POST")
            {
                request = (HttpWebRequest)WebRequest.Create(url);
                request.Method = method;
                
                if (!string.IsNullOrEmpty(Authorization))
                {
                    request.Headers[HttpRequestHeader.Authorization] = Authorization;
                }

                if (PostContentType == ContentType.APPLICATION_X_WWW_FORM_URLENCODED)
                {
                    request.ContentType = "application/x-www-form-urlencoded";
                }
                else
                {
                    _postBoundary = GetPostBoundary();
                    request.ContentType = string.Format("multipart/form-data; boundary={0}", _postBoundary);
                }
                
                request.BeginGetRequestStream(new AsyncCallback(GetRequestStreamCallback), request);
            }
            else
            {
                StringBuilder urlBuilder = new StringBuilder(url);
                urlBuilder.Append("?");
                foreach (var item in Parameters)
                {
                    urlBuilder.Append(item.Name);
                    urlBuilder.Append("=");
                    urlBuilder.Append(item.Value);
                    urlBuilder.Append("&");
                }
                url = urlBuilder.ToString();
                
                request = (HttpWebRequest)WebRequest.Create(url);
                if (!string.IsNullOrEmpty(Authorization))
                {
                    request.Headers[HttpRequestHeader.Authorization] = Authorization;
                }

                request.BeginGetResponse(new AsyncCallback(ResponseCallback), request);
            }
        }


        private void GetRequestStreamCallback(IAsyncResult asynchronousResult)
        {
            RequestCompletedEventArgs args = new RequestCompletedEventArgs();
            args.UserData = _userData;
            try
            {
                HttpWebRequest request = (HttpWebRequest)asynchronousResult.AsyncState;

                using (Stream postStream = request.EndGetRequestStream(asynchronousResult))
                {
                    if (PostContentType == ContentType.MULTIPART_FORM_DATA)
                    {
                        WritePostData(_postBoundary, postStream);
                    }
                    else
                    {
                        StringBuilder parmsBuilder = new StringBuilder(string.Empty);
                        foreach (var item in Parameters)
                        {
                            parmsBuilder.Append(item.Name);
                            parmsBuilder.Append("=");
                            parmsBuilder.Append(item.Value);
                            parmsBuilder.Append("&");
                        }

                        byte[] buffer = Encoding.UTF8.GetBytes(parmsBuilder.ToString());
                        postStream.Write(buffer, 0, buffer.Length);
                    }

                }

                request.BeginGetResponse(new AsyncCallback(ResponseCallback), request);
            }
            catch (WebException ex)
            {
                args.Error = ex;
                if (RequestCompleted != null)
                    RequestCompleted(args);
            }
        }

        string CreateImageName(byte[] bytes)
        {
            StringBuilder nameBuilder = new StringBuilder("shareimg");
            ImageType type = ImageTypeHelper.GetImageType(bytes);
            switch (type)
            {
                case ImageType.BMP:
                    nameBuilder.Append(".bmp");
                    break;
                case ImageType.GIF:
                    nameBuilder.Append(".gif");
                    break;
                case ImageType.PNG:
                    nameBuilder.Append(".png");
                    break;
                case ImageType.JPEG:
                default:
                    nameBuilder.Append(".jpg");
                    break;
            }
            return nameBuilder.ToString();
        }

        private void ResponseCallback(IAsyncResult asynchronousResult)
        {
            RequestCompletedEventArgs args = new RequestCompletedEventArgs();
            args.UserData = _userData;
            try
            {
                HttpWebRequest request = (HttpWebRequest)asynchronousResult.AsyncState;
                HttpWebResponse response = (HttpWebResponse)request.EndGetResponse(asynchronousResult);

                Stream stream = response.GetResponseStream();
                StreamReader reader = new StreamReader(stream);
                String stringContent = reader.ReadToEnd();
                args.Result = stringContent;
                stream.Close();
            }
            catch (WebException ex)
            {
                args.Error = ex;
            }

            if (RequestCompleted != null)
                RequestCompleted(args);

        }

        /// <summary>
        /// 写post数据流
        /// </summary>
        /// <param name="boundary"></param>
        /// <param name="postStream"></param>
        private void WritePostData(string boundary, Stream postStream)
        {
            StreamWriter sw = new StreamWriter(postStream);
            string header = string.Format("--{0}", boundary);
            string footer = string.Format("--{0}--", boundary);

            foreach (RequestParameter p in Parameters)
            {
                sw.WriteLine(header);
                if (!p.IsBinaryData)
                {
                    var value = string.Format("{0}", p.Value);
                    if (!string.IsNullOrEmpty(value))
                    {
                        sw.WriteLine(@"Content-Disposition: form-data; name=""{0}""", p.Name);
                        sw.WriteLine();
                        sw.WriteLine(value);
                    }
                }
                else
                {
                    byte[] file = (byte[])p.Value;
                    sw.WriteLine(@"Content-Disposition: form-data; name=""{0}""; filename=""{1}""", p.Name, CreateImageName(file));
                    sw.WriteLine(@"Content-Type: application/octet-stream");
                    sw.WriteLine(@"Content-Length: " + file.Length);
                    sw.WriteLine();
                    sw.Flush();
                    Stream bs = sw.BaseStream;
                    bs.Write(file, 0, file.Length);
                    bs.Flush();
                    sw.WriteLine();
                }
            }

            sw.WriteLine(footer);
            sw.Flush();
            sw.Close();
        }

        /// <summary>
        /// 获取http post头的boundary
        /// </summary>
        /// <returns></returns>
        private string GetPostBoundary()
        {
            string pattern = "abcdefghijklmnopqrstuvwxyz0123456789";
            StringBuilder boundaryBuilder = new StringBuilder("----");
            Random rnd = new Random();
            for (int i = 0; i < 10; i++)
            {
                var index = rnd.Next(pattern.Length);
                boundaryBuilder.Append(pattern[index]);
            }
            return boundaryBuilder.ToString();
        }
    }

    internal class RequestCompletedEventArgs
    {
        /// <summary>
        /// 获取的web内容
        /// </summary>
        public String Result { set; get; }

        /// <summary>
        /// 获取或设置在获取web内容中发生的异常
        /// </summary>
        public Exception Error { set; get; }

        public object UserData { set; get; }

    }

    /// <summary>
    /// 获取web内容完成事件
    /// </summary>
    internal delegate void RequestCompletedEventHandler(RequestCompletedEventArgs e);
}
